home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / internet / other / ka9q / nhclb120.zoo / ax25cmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-18  |  13.9 KB  |  709 lines

  1. #include <stdio.h>
  2. #include "global.h"
  3. #include "config.h"
  4. #include "mbuf.h"
  5. #include "ax25.h"
  6. #include "ax_mbx.h"
  7. #include "timer.h"
  8. #include "netuser.h"
  9. #include "ftp.h"
  10. #include "tcp.h"
  11. #include "telnet.h"
  12. #include "iface.h"
  13. #include "lapb.h"
  14. #include "finger.h"
  15. #include "cmdparse.h"
  16. #include "session.h"
  17. #include "nr4.h"
  18. #ifdef    UNIX
  19. #include <string.h>
  20. #include <sys/types.h>
  21. time_t time();
  22. #endif
  23. /* heard stuff */
  24. #include "heard.h"
  25. /* heard stuff */
  26. static dumpstat();
  27.  
  28. char *ax25states[] = {
  29.     "Disconnected",
  30.     "Conn pending",
  31.     "Disc pending",
  32.     "Connected",
  33.     "Recovery",
  34.     "Frame Reject",
  35. };
  36.  
  37. int domycall(),dodigipeat(),doaxstat(),dot1(),dot2(),dot3(),domaxframe(),
  38.     doaxwindow(),dopaclen(),don2(),doaxreset(),dopthresh(),doheard();
  39. #ifdef SID2
  40. int dobbscall();
  41. extern struct ax25_addr bbscall;
  42. #endif
  43.  
  44. static struct cmds axcmds[] = {
  45.     "digipeat",    dodigipeat,    0, NULLCHAR,    NULLCHAR,
  46.     "maxframe",    domaxframe,    0, NULLCHAR,    NULLCHAR,
  47.     "mycall",    domycall,    0, NULLCHAR,    NULLCHAR,
  48. #ifdef SID2
  49.     "bbscall",    dobbscall,    0, NULLCHAR,    NULLCHAR,
  50. #endif
  51.     "paclen",    dopaclen,    0, NULLCHAR,    NULLCHAR,
  52.     "pthresh",    dopthresh,    0, NULLCHAR,    NULLCHAR,
  53.     "reset",    doaxreset,    2, "ax25 reset <axcb>", NULLCHAR,
  54.     "retry",    don2,        0, NULLCHAR,    NULLCHAR,
  55.     "status",    doaxstat,    0, NULLCHAR,    NULLCHAR,
  56.     "t1",        dot1,        0, NULLCHAR,    NULLCHAR,
  57.     "t2",        dot2,        0, NULLCHAR,    NULLCHAR,
  58.     "t3",        dot3,        0, NULLCHAR,    NULLCHAR,
  59.     "window",    doaxwindow,    0, NULLCHAR,    NULLCHAR,
  60. /* heard stuff */
  61.     "heard",    doheard,    0, NULLCHAR,    NULLCHAR,
  62. /* heard stuff */
  63. #ifdef SID2
  64.     NULLCHAR,    NULLFP,        0, "ax25 subcommands: bbscall digipeat heard maxframe mycall paclen pthresh reset\n\t\tretry status t1 t2 t3 window",    NULLCHAR,
  65. #else
  66.     NULLCHAR,    NULLFP,        0, "ax25 subcommands: digipeat heard maxframe mycall paclen pthresh reset retry\n\t\tstatus t1 t2 t3 window",    NULLCHAR,
  67. #endif
  68. };
  69.  
  70. /* Multiplexer for top-level ax25 command */
  71. doax25(argc,argv)
  72. int argc;
  73. char *argv[];
  74. {
  75.     return subcmd(axcmds,argc,argv);
  76. }
  77.  
  78. /*ARGSUSED*/
  79. static
  80. doaxreset(argc,argv)
  81. int argc;
  82. char *argv[];
  83. {
  84.     struct ax25_cb *axp;
  85.     extern char notval[];
  86.  
  87.     axp = (struct ax25_cb *)htol(argv[1]);
  88.     if(!ax25val(axp)){
  89.         printf(notval);
  90.         return 1;
  91.     }
  92.     reset_ax25(axp);
  93.     return 0;
  94. }
  95.  
  96. /* Display AX.25 link level control blocks */
  97. static
  98. doaxstat(argc,argv)
  99. int argc;
  100. char *argv[];
  101. {
  102.     register int i;
  103.     register struct ax25_cb *axp;
  104.     char tmp[10];
  105.     extern char notval[];
  106.  
  107.     if(argc < 2){
  108.         printf("    &AXB IF   Snd-Q   Rcv-Q   Remote    State\n");
  109.         for(i=0;i<NHASH;i++){
  110.             for(axp = ax25_cb[i];axp != NULLAX25; axp = axp->next){
  111.                 pax25(tmp,&axp->addr.dest);
  112.                 printf("%8lx %-5s%-8d%-8d%-10s%s\n",
  113.                     (long)axp,axp->interface->name,
  114.                     len_q(axp->txq),len_mbuf(axp->rxq),
  115.                     tmp,ax25states[axp->state]);
  116.             }
  117.         }
  118.         return 0;
  119.     }
  120.     axp = (struct ax25_cb *)htol(argv[1]);
  121.     if(!ax25val(axp)){
  122.         printf(notval);
  123.         return 1;
  124.     }
  125.     dumpstat(axp);
  126.     return 0;
  127. }
  128. /* Dump one control block */
  129. static
  130. dumpstat(axp)
  131. register struct ax25_cb *axp;
  132. {
  133.     char tmp[10];
  134.     int i;
  135.  
  136.     if(axp == NULLAX25 || axp->interface == NULLIF)
  137.         return;
  138.     printf("&AXB IF   Remote   RB V(S) V(R) Unack P Retry State\n");
  139.     pax25(tmp,&axp->addr.dest);
  140.     printf("%4x %-5s%-9s",(int)axp,axp->interface->name,tmp);
  141.     putchar(axp->rejsent ? 'R' : ' ');
  142.     putchar(axp->remotebusy ? 'B' : ' ');
  143.     printf(" %4d %4d",axp->vs,axp->vr);
  144.     printf(" %02u/%02u %u",axp->unack,axp->maxframe,axp->proto);
  145.     printf(" %02u/%02u",axp->retries,axp->n2);
  146.     printf(" %s\n",ax25states[axp->state]);
  147.  
  148.     printf("T1: ");
  149.     if(run_timer(&axp->t1))
  150.         printf("%lu",(axp->t1.start - axp->t1.count) * MSPTICK);
  151.     else
  152.         printf("stop");
  153.     printf("/%lu ms; ",axp->t1.start * MSPTICK);
  154.  
  155.     printf("T2: ");
  156.     if(run_timer(&axp->t2))
  157.         printf("%lu",(axp->t2.start - axp->t2.count) * MSPTICK);
  158.     else
  159.         printf("stop");
  160.     printf("/%lu ms; ",axp->t2.start * MSPTICK);
  161.  
  162.     printf("T3: ");
  163.     if(run_timer(&axp->t3))
  164.         printf("%lu",(axp->t3.start - axp->t3.count) * MSPTICK);
  165.     else
  166.         printf("stop");
  167.     printf("/%lu ms\n",axp->t3.start * MSPTICK);
  168.  
  169.     if(axp->addr.ndigis == 0)
  170.         return;
  171.     printf("Digipeaters:");
  172.     for(i=0;i<axp->addr.ndigis;i++){
  173.         pax25(tmp,&axp->addr.digis[i]);
  174.         printf(" %s",tmp);
  175.     }
  176.     printf("\n");
  177. }
  178.  
  179. /* Display or change our AX.25 address */
  180. static
  181. domycall(argc,argv)
  182. int argc;
  183. char *argv[];
  184. {
  185.     char buf[15];
  186.  
  187.     if(argc < 2){
  188.         pax25(buf,&mycall);
  189.         printf("%s\n",buf);
  190.         return 0;
  191.     }
  192.     if(setcall(&mycall,argv[1]) == -1)
  193.         return -1;
  194.     mycall.ssid |= E;
  195.     return 0;
  196. }
  197.  
  198. #ifdef SID2
  199. /* Display or change our AX.25 BBS address */
  200. static
  201. dobbscall(argc,argv)
  202. int argc;
  203. char *argv[];
  204. {
  205.     char buf[15];
  206.  
  207.     if(argc < 2){
  208.         pax25(buf,&bbscall);
  209.         printf("%s\n",buf);
  210.         return 0;
  211.     }
  212.     if(setcall(&bbscall,argv[1]) == -1)
  213.         return -1;
  214.     bbscall.ssid |= E;
  215.     return 0;
  216. }
  217. #endif
  218.  
  219. /* Control AX.25 digipeating */
  220. static
  221. dodigipeat(argc,argv)
  222. int argc;
  223. char *argv[];
  224. {
  225.     extern int digipeat;
  226.  
  227.     if(argc == 1) {
  228.         printf("digipeat %s\n",digipeat ? "on" : "off");
  229.     } else {
  230.         if(strcmp(argv[1],"on") == 0)
  231.             digipeat = 1;
  232.         else
  233.             digipeat = 0;
  234.     }
  235. }
  236.  
  237. /* Set retransmission timer */
  238. static
  239. dot1(argc,argv)
  240. int argc;
  241. char *argv[];
  242. {
  243.     extern int16 t1init;
  244.     long atol();
  245.  
  246.     if(argc == 1) {
  247.         printf("T1 %lu ms\n",(long)t1init * MSPTICK);
  248.     } else {
  249.         t1init = (int16)atol(argv[1]) / MSPTICK;
  250.     }
  251. }
  252.  
  253. /* Set acknowledgement delay timer */
  254. static
  255. dot2(argc,argv)
  256. int argc;
  257. char *argv[];
  258. {
  259.     extern int16 t2init;
  260.     long atol();
  261.  
  262.     if(argc == 1) {
  263.         printf("T2 %lu ms\n",(long)t2init * MSPTICK);
  264.     } else {
  265.         t2init = (int16)atol(argv[1]) / MSPTICK;
  266.     }
  267. }
  268.  
  269. /* Set idle timer */
  270. static
  271. dot3(argc,argv)
  272. int argc;
  273. char *argv[];
  274. {
  275.     extern int16 t3init;
  276.     long atol();
  277.  
  278.     if(argc == 1) {
  279.         printf("T3 %lu ms\n",(long)t3init * MSPTICK);
  280.     } else {
  281.         t3init = (int16)atol(argv[1]) / MSPTICK;
  282.     }
  283. }
  284.  
  285. /* Set retry limit count */
  286. static
  287. don2(argc,argv)
  288. int argc;
  289. char *argv[];
  290. {
  291.     extern int16 n2;
  292.  
  293.     if(argc == 1) {
  294.         printf("Retry %u\n",n2);
  295.     } else {
  296.         n2 = atoi(argv[1]);
  297.     }
  298. }
  299.  
  300. /* Set maximum number of frames that will be allowed in flight */
  301. static
  302. domaxframe(argc,argv)
  303. int argc;
  304. char *argv[];
  305. {
  306.     extern int16 maxframe;
  307.  
  308.     if(argc == 1) {
  309.         printf("Maxframe %u\n",maxframe);
  310.     } else {
  311.         maxframe = atoi(argv[1]);
  312.     }
  313. }
  314.  
  315. /* Set maximum length of I-frame data field */
  316. static
  317. dopaclen(argc,argv)
  318. int argc;
  319. char *argv[];
  320. {
  321.     extern int16 paclen;
  322.  
  323.     if(argc == 1) {
  324.         printf("Paclen %u\n",paclen);
  325.     } else {
  326.         paclen = atoi(argv[1]);
  327.     }
  328. }
  329. /* Set size of I-frame above which polls will be sent after a timeout */
  330. static
  331. dopthresh(argc,argv)
  332. int argc;
  333. char *argv[];
  334. {
  335.     extern int16 pthresh;
  336.  
  337.     if(argc == 1) {
  338.         printf("Pthresh %u\n",pthresh);
  339.     } else {
  340.         pthresh = atoi(argv[1]);
  341.     }
  342. }
  343.  
  344. /* Set high water mark on receive queue that triggers RNR */
  345. static
  346. doaxwindow(argc,argv)
  347. int argc;
  348. char *argv[];
  349. {
  350.     extern int16 axwindow;
  351.  
  352.     if(argc == 1) {
  353.         printf("Axwindow %u\n",axwindow);
  354.     } else {
  355.         axwindow = atoi(argv[1]);
  356.     }
  357. }
  358. /* End of ax25 subcommands */
  359.  
  360. /* Initiate interactive AX.25 connect to remote station */
  361. doconnect(argc,argv)
  362. int argc;
  363. char *argv[];
  364.  {
  365.     void ax_rx(),ax_tx(),ax_state();
  366.     int ax_parse();
  367.     struct ax25_addr dest;
  368.     struct ax25 addr;
  369.     struct ax25_cb *open_ax25();
  370.     struct interface *ifp;
  371.     struct session *s;
  372.     extern int16 axwindow;
  373.     int i;
  374.  
  375.     if (strcmp(argv[1],"netrom") == 0) {
  376.         printf("Connect on netrom interface not supported\n") ;
  377.         return 1 ;
  378.     }
  379.     
  380.     for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
  381.         if(strcmp(argv[1],ifp->name) == 0)
  382.             break;
  383.  
  384.     if(ifp == NULLIF){
  385.         printf("Interface %s unknown\n",argv[1]);
  386.         return 1;
  387.     }
  388.     setcall(&dest,argv[2]);
  389.     /* See if a session already exists */
  390.     for(s = sessions; s < &sessions[nsessions]; s++){
  391.         if(s->type == AX25TNC
  392.          && addreq(&s->cb.ax25_cb->addr.dest,&dest)){
  393. #if    (defined(MAC) || defined(AMIGA))
  394.             printf("Session %lu to %s already exists\n",
  395. #else
  396.             printf("Session %u to %s already exists\n",
  397. #endif
  398.                 s - sessions,argv[2]);
  399.             return 1;
  400.         }
  401.     }
  402.     /* Allocate a session descriptor */
  403.     if((s = newsession()) == NULLSESSION){
  404.         printf("Too many sessions\n");
  405.         return 1;
  406.     }
  407.     if((s->name = malloc((unsigned)strlen(argv[2])+1)) != NULLCHAR)
  408.         strcpy(s->name,argv[2]);
  409.     s->type = AX25TNC;
  410.     s->parse = ax_parse;
  411.     current = s;
  412.     ASSIGN(addr.source,mycall);
  413.     setcall(&addr.dest,argv[2]);
  414.     for(i=3; i < argc; i++)
  415.         setcall(&addr.digis[i-3],argv[i]);
  416.  
  417.     addr.ndigis = i - 3;
  418.     s->cb.ax25_cb = open_ax25(&addr,axwindow,ax_rx,ax_tx,ax_state,ifp,(char *)s);
  419.     go();
  420.     return 0;
  421. }
  422.  
  423.  
  424. /* Display changes in AX.25 state */
  425. void
  426. ax_state(axp,old,new)
  427. struct ax25_cb *axp;
  428. int old,new;
  429. {
  430.     struct session *s;
  431.  
  432.     s = (struct session *)axp->user;
  433.  
  434.     if(current != NULLSESSION && current->type == AX25TNC && current == s){
  435.         /* Don't print transitions between CONNECTED and RECOVERY */
  436.         if(new != RECOVERY && !(old == RECOVERY && new == CONNECTED))
  437.             printf("%s\n",ax25states[new]);
  438.         if(new == DISCONNECTED)
  439.             cmdmode();
  440.         fflush(stdout);
  441.     }
  442.     if(new == DISCONNECTED){
  443.         axp->user = NULLCHAR;
  444.         freesession(s);
  445.     }
  446. }
  447. /* Handle typed characters on AX.25 connection */
  448. int
  449. ax_parse(buf,cnt)
  450. char *buf;
  451. int16 cnt;
  452. {
  453.     struct mbuf *bp;
  454.     register char *cp;
  455.     char c;
  456.  
  457.     if(current == NULLSESSION || current->type != AX25TNC)
  458.         return;    /* "can't happen" */
  459.  
  460.     /* If recording is on, record outgoing stuff too */
  461.     if(current->record != NULLFILE)
  462.         fwrite(buf,1,(int)cnt,current->record);
  463.  
  464.     /* Allocate buffer and start it with the PID */
  465.     bp = alloc_mbuf(cnt+1);
  466.     *bp->data = PID_FIRST | PID_LAST | PID_NO_L3;
  467.     bp->cnt++;
  468.  
  469.     /* Copy keyboard buffer to output, stripping line feeds */
  470.     cp = bp->data + 1;
  471.     while(cnt-- != 0){
  472.         c = *buf++;
  473.         if(c != '\n'){
  474.             *cp++ = c;
  475.             bp->cnt++;
  476.         }
  477.     }
  478.     send_ax25(current->cb.ax25_cb,bp);
  479. }
  480. /* Handle new incoming terminal sessions
  481.  * This is the default receive upcall function, used when
  482.  * someone else connects to us
  483.  */
  484. void
  485. ax_incom(axp,cnt)
  486. register struct ax25_cb *axp;
  487. int16 cnt;
  488. {
  489.     void ax_session(), mbx_incom() ;
  490.     
  491. #ifdef    SID2
  492.     ax_session(axp, cnt);
  493. #else
  494.     if (ax25mbox)
  495.         mbx_incom(axp,cnt) ;
  496.     else
  497.         ax_session(axp,cnt) ;
  498. #endif
  499.     return ;
  500.  
  501. }
  502.  
  503. /* This function sets up an ax25 chat session */
  504. /*ARGSUSED*/
  505. void
  506. ax_session(axp,cnt)
  507. register struct ax25_cb *axp ;
  508. int16 cnt ;
  509. {
  510.     struct session *s;
  511.     char remote[10];
  512.     void ax_rx(),ax_state(),ax_tx();
  513.  
  514.     pax25(remote,&axp->addr.dest);
  515.     if((s = newsession()) == NULLSESSION){
  516.         /* Out of sessions */
  517.         disc_ax25(axp);
  518.         return;
  519.     }
  520.     s->type = AX25TNC;
  521.     s->name = malloc((int16)strlen(remote)+1);
  522.     s->cb.ax25_cb = axp;
  523.     s->parse = ax_parse;
  524.     strcpy(s->name,remote);
  525.     axp->r_upcall = ax_rx;
  526.     axp->s_upcall = ax_state;
  527.     axp->t_upcall = ax_tx;
  528.     axp->user = (char *)s;
  529. #if    (defined(MAC) || defined(AMIGA))
  530.     printf("\007Incoming AX25 session %lu from %s\n",s - sessions,remote);
  531. #else
  532.     printf("\007Incoming AX25 session %u from %s\n",s - sessions,remote);
  533. #endif
  534.     fflush(stdout);
  535. }
  536.  
  537. /* Handle incoming terminal traffic */
  538. void
  539. ax_rx(axp,cnt)
  540. struct ax25_cb *axp;
  541. int16 cnt;
  542. {
  543.     register struct mbuf *bp;
  544.     struct mbuf *recv_ax25();
  545.     char c;
  546.  
  547.     /* Hold output if we're not the current session */
  548.     if(mode != CONV_MODE || current == NULLSESSION
  549.      || current->type != AX25TNC || current->cb.ax25_cb != axp)
  550.         return;
  551.  
  552.     if((bp = recv_ax25(axp,cnt)) == NULLBUF)
  553.         return;
  554.  
  555.     /* Display received characters, translating CR's to CR/LF */
  556.     while(bp != NULLBUF){
  557.         while(bp->cnt-- != 0){
  558.             c = *bp->data++;
  559.             if(c == '\r')
  560.                 c = '\n';
  561.             putchar(c);
  562.             if(current->record){
  563. #ifndef UNIX
  564.                 if(c == '\n')
  565.                     fputc('\r',current->record);
  566. #endif
  567.                 fputc(c,current->record);
  568.             }
  569.         }
  570.         bp = free_mbuf(bp);
  571.     }
  572.     if(current->record)
  573.         fflush(current->record);
  574.     fflush(stdout);
  575. }
  576. /* Handle transmit upcalls. Used only for file uploading */
  577. void
  578. ax_tx(axp,cnt)
  579. struct ax25_cb *axp;
  580. int16 cnt;
  581. {
  582.     register char *cp;
  583.     struct session *s;
  584.     register struct mbuf *bp;
  585.     int16 size;
  586.     int c;
  587.  
  588.     if((s = (struct session *)axp->user) == NULLSESSION
  589.      || s->upload == NULLFILE)
  590.         return;
  591.     while(cnt != 0){
  592.         size = min(cnt,axp->paclen+1);
  593.         if((bp = alloc_mbuf(size)) == NULLBUF)
  594.             break;
  595.         cp = bp->data;
  596.         /* Start with the PID */
  597.         *cp++ = PID_FIRST | PID_LAST | PID_NO_L3;
  598.         bp->cnt++;
  599.  
  600.         /* Now send data characters, translating between local
  601.          * keyboard end-of-line sequences and the (unwritten)
  602.          * AX.25 convention, which is carriage-return only
  603.          */
  604.         while(bp->cnt < size){
  605.             if((c = getc(s->upload)) == EOF)
  606.                 break;
  607. #ifdef    MSDOS
  608.             /* MS-DOS gives cr-lf */
  609.             if(c == '\n')
  610.                 continue;
  611. #endif
  612. #if    (defined(UNIX) || defined(MAC) || defined(AMIGA))
  613.             /* These give lf only */
  614.             if(c == '\n')
  615.                 c = '\r';
  616. #endif
  617.             *cp++ = c;
  618.             bp->cnt++;
  619.         }    
  620.         if(bp->cnt > 1) {
  621.             send_ax25(axp,bp);
  622.         } else {
  623.             /* Empty frame, don't bother sending */
  624.             free_p(bp);
  625.             break;
  626.         }
  627.         cnt -= bp->cnt;
  628.     }
  629.     if(cnt != 0){
  630.         /* Error or end-of-file */
  631.         fclose(s->upload);
  632.         s->upload = NULLFILE;
  633.         free(s->ufile);
  634.         s->ufile = NULLCHAR;
  635.     }
  636. }
  637.  
  638. /* heard stuff */
  639. struct ax25_heard heard = {0,0};
  640. /* Display AX.25 heard list */
  641. static
  642. doheard(argc,argv)
  643. int argc;
  644. char *argv[];
  645. {
  646.     struct heard_stuff *hp;
  647.     struct ax25_addr *dp;
  648.     extern struct ax25_heard heard;
  649.     char tmp[16];
  650.     int curr;
  651.     long t;
  652.  
  653.     if(argc < 2) {
  654.         if (!heard.enabled) {
  655.             printf("not enabled\n");
  656.             return(0);
  657.         }
  658.         time(&t);
  659.         printf("\nHeard list: %s\n",ctime(&t));
  660.         curr = heard.first;
  661.         while (curr != -1) {
  662.             hp = &heard.list[curr];
  663.             pax25(tmp,&hp->info.source);
  664.             printf("%-10s",tmp);
  665.             if(hp->info.ndigis > 0){
  666.                 printf(" (via");
  667.                 /* Print digi string until packet got to us */
  668.                 for(dp = &hp->info.digis[0]; dp < &hp->info.digis[hp->info.ndigis]; dp++){
  669.                     if (dp->ssid & REPEATED) {
  670.                         pax25(tmp,dp);
  671.                         printf(" %s",tmp);
  672.                     } else {
  673.                         break;
  674.                     }
  675.                 }
  676.                 printf(")");
  677.             }
  678.             if (hp->flags) {
  679.                 printf("%-5s%-8s%-4s",
  680.                                 (hp->flags & HEARD_ARP) ? " ARP" : "",
  681.                                 (hp->flags & HEARD_NETROM) ? " NETROM" : "",
  682.                                 (hp->flags & HEARD_IP) ? " IP" : "");
  683.             }
  684.             printf("  %s", ctime((long *)&hp->htime));
  685.             curr = hp->next;
  686.         }
  687.         return 0;
  688.     }
  689.     if (strcmp(argv[1],"clear") == 0) {
  690.         heard.cnt = 0;
  691.         heard.first = -1;
  692.         return 0;
  693.     } else if (strcmp(argv[1],"on") == 0) {
  694.         if (!heard.enabled) {
  695.             heard.enabled = 1;
  696.             heard.cnt = 0;
  697.             heard.first = -1;
  698.         }
  699.         return 0;
  700.     } else if (strcmp(argv[1],"off") == 0) {
  701.         heard.enabled = 0;
  702.         return 0;
  703.     } else {
  704.         printf("Usage: ax25 heard [ on | off | clear ]\n");
  705.     }
  706.     return 0;
  707. }
  708. /* heard stuff */
  709.